Introducción

El cómputo en unidades de procesamiento gráfico (GPU por sus siglas en inglés) pertenece a una de las tendencias más nuevas en el mundo de la ciencia computacional. Su atractivo realmente reside en el hecho de que actualmente las tarjetas gráficas cuentan con un gran poder computacional. Por poner un ejemplo, al momento de escribir esto (junio de 2015) la tarjeta Tesla K80 de NVIDIA que se muestra en la figura 1 (está guapa ¿No?) tiene 4992 núcleos y 24 GB de memoria RAM (tres veces la memoria de la laptop en que se escribe esto), alcanzando un rendimiento computacional pico de 8.74 Tflops (flop es un acrónimo en inglés que viene de floating-point operations per second, y mide la cantidad de operaciones que una computadora realiza por segundo utilizando números de punto flotante) en aritmética de precisión simple. Para poner éste resultado en contexto Kan Balam la supercomputadora instalada en 2007 en la UNAM alcanzaba en su pico 7.113 Tflops, es decir que contaba con $\approx$ (aproximadamente) 0.8138 veces el poder computacional de la Tesla K80; la actual supercomputadora de la UNAM Miztli alcanza un pico de rendimiento de 21.3 Tflops esto es $\approx$ 2.43 veces el poder computacional de la Tesla K80.

Figura 1: Tesla K80 (imagen tomada de la página de NVIDIA)

Aunque, es necesario hacer primero una aclaración, el poder de las supercomputadoras anteriormente mencionadas viene dado principalmente por una cantidad enorme de procesadores, ¿cómo se compara el rendimiento de un CPU contra un GPU? sería la siguiente pregunta. Como ejemplo inmediato, hacemos referencia a la figura 2

Figura 2: Comparación del rendimiento al utilizar aplicaciones científicas (imagen tomada de la página de NVIDIA)

aquí se puede obervar por ejemplo que para paquetes como Quantum Espresso la diferencia es muy poca y da lo mismo la elección que se haga (posteriormente hablaremos de por qué pasa eso), mientras que para paquetes como Caffe utilizados en machine learning se obtiene una mejora por un factor de 24 veces el rendimiento en CPU.

Sin embargo, antes de sucumbir a la emoción es necesario hacer ciertas aclaraciones. En primer lugar, estas tarjetas son demasiado caras, lo que significa que un adolescente no es capaz de comprarlas con lo que le paga su papá al lavar el coche. La tarjeta que se muestra en la figura 1 tiene como precio de lanzamiento $5,000 y no son pesos, sino dólares. De esta manera, el cómputo intensivo en GPUs se sale incluso del alcance del ama de casa promedio; así, ésta clase de cómputo se restringe, es decir, no se encuentra a la mano de todo aquél que desee experimentar con ella.

Después de leer esto, el lector avispado se encontrará con una pregunta en mente, que es; "si el cómputo en tarjetas gráficas es tan genial ¿por qué no se hacen todos los cálculos utlizando GPUs?". Bueno, en realidad es una duda válida, es lo que todos al principio se cuestionan; ¿por qué los gobiernos y las universidades invierten millones y millones para construir supercomputadoras cuando con tan sólo $5,000 dólares pueden tener al alcance un gran poder computacional? ¿Es que acaso quieren gastarse el dinero de los proyectos que les aprueban? ¿Quieren acabar rápidamente con el presupuesto que le asignan a la ciencia? ¿Es un complot del estado para hacer más ricas a las grandes compañías que producen y distribuyen hardware? Pues no; no es un complot, algo que no se debe perder de vista es que tanto los CPU como los GPU son herramientas y como herramientas están diseñadas para ser usadas en cierto tipo de circunstancias y cada una es óptima para resolver cierto tipo de problemas.

No cavan con cucharas pozos para plantar árboles ¿cierto?

Paralelismo

Figura 3: Biblioteca Central de la U.N.A.M (imagen tomada de https://www.flickr.com/photos/deiv_id/4101980763)

En la figura 3 podemos ver el edificio de la Biblioteca Central de la UNAM, famosa por sus fachadas diseñadas y realizadas por el arquitecto mexicano Juan O'Gorman. Las cuatro fachadas cubren un total de 4,000 metros cuadrados. Ahora, según O'Gorman, los pedazos de piedra utilizados como mosaicos miden entre 2 y 4 centímetros de lado [1], para fines didácticos vamos a suponer que todos miden 4 centímetros cuadrados.

Unos rápidos cálculos con ésta suposición nos dicen que cubriendo las fachadas de la biblioteca hay 10,000,000 de mosaicos. Ahora, supongamos que el arquitecto quería hacer esto sin ayuda de nadie más, pegando uno por uno los mosaicos en su lugar. Si fuera tal el caso, entonces estaríamos convencidos totalmente de que O'Gorman trabaja como CPU, toma una tarea repetitiva y la realiza un sinfín de veces hasta que termina, no puede realizar tareas simultáneas, por lo que sólo pega un mosaico a la vez (nada de usar las dos manos, está prohibido).

Introduciremos ahora dos conceptos que nos van a ser útiles, definimos latencia como la cantidad de tiempo necesaria para terminar una tarea, y rendimiento como el número de acciones realizadas entre el tiempo que toma llevarlas a cabo.

En el caso de O'Gorman CPU podemos calcular la latencia y el rendimiento del proceso de rellenar la fachada. Si suponemos que pegar un mosaico toma 10 segundos entonces la latencia es de 100,000,000 s (aproximadamente 3 años y 2 meses) mientras que el rendimiento sería de 0.1 mosaicos/segundo.

Abajo definimos una función en Python para calcular el tiempo necesario para terminar la decoración de las fachadas si se tiene cierto número de trabajadores pegando mosaicos a una velocidad dada y hacemos el cálculo del ejemplo anterior.


In [1]:
segundos_por_anio = 60.*60.*24.*365.

def calcular_tiempo_de_construccion(velocidad, trabajadores):
    rendimiento = velocidad*trabajadores
    tiempo_total = (10000000./velocidad)/trabajadores
    anios = int(tiempo_total/(segundos_por_anio))
    meses = int((tiempo_total/(segundos_por_anio) % 1)*12.)
    dias = int(((tiempo_total/(segundos_por_anio) % 1)*12. - meses)*30.)
    horas = int((((tiempo_total/(segundos_por_anio) % 1)*12. - meses)*30. - dias)*24)
    print"A una velocidad de ", velocidad, "mosaicos/segundo, ", trabajadores,\
           " trabajador(es) termina(n) la decoración en ", anios ," año(s)," , meses, "mes(es), ",\
           dias, "día(s) y ", horas, "hora(s)."
    print "El rendimiento es de ", rendimiento, " mosaico(s) por segundo."

In [2]:
velocidad = 0.1 #velocidad en mosaicos por segundo
trabajadores = 1

calcular_tiempo_de_construccion(velocidad,trabajadores)


A una velocidad de  0.1 mosaicos/segundo,  1  trabajador(es) termina(n) la decoración en  3  año(s), 2 mes(es),  1 día(s) y  13 hora(s).
El rendimiento es de  0.1  mosaico(s) por segundo.

Es obvio que la Biblioteca Central no fue decorada de ésta manera. No fue una sola persona sino 40 albañiles [1] los que decoraron las fachadas, ahora supongamos que dado que eran muchos, no tenían el problema de que se les acabaría la vida pegando mosaicos y por lo tanto llevaban un ritmo más relajado, digamos que pegaban un mosaico cada 40 segundos la latencia sería en éste caso el numero de mosaicos por la cantidad de tiempo que se tardan en pegar un mosaico dividido entre el número de albañiles que equivale a 10,000,000 s (aproximadamente 4 meses), mientras que el rendimiento sería de 1 mosaico por segundo.


In [3]:
velocidad = 1./40.
trabajadores = 40

calcular_tiempo_de_construccion(velocidad,trabajadores)


A una velocidad de  0.025 mosaicos/segundo,  40  trabajador(es) termina(n) la decoración en  0  año(s), 3 mes(es),  24 día(s) y  3 hora(s).
El rendimiento es de  1.0  mosaico(s) por segundo.

La siguiente celda es para que el lector pruebe diferentes combinaciones de velocidad y número de trabajadores, las celdas en el notebook de iPython se ejecutan de distintas maneras

  • Presionando el botón de play de la barra superior al estar posicionado en la celda.
  • Tecleando shift + enter (ejecuta y se mueve hacia abajo)
  • Tecleando ctrl + enter (ejecuta y no se mueve)
  • Tecleando alt + enter (ejecuta e inserta una celda abajo)

In [ ]:
velocidad = 
trabajadores = 

calcular_tiempo_de_construccion(velocidad,trabajadores)

Con éste ejemplo vemos que en ocasiones es más eficiente utilizar un montón de trabajadores lentos haciendo tareas al mismo tiempo que utilizar un trabajador muy rápido realizando tareas solo.

Pero de nuevo hay ciertas cosas que hemos pasado por alto. El hecho de que se pudieran tener a 40 albañiles trabajando al mismo tiempo es debido principalemente a dos circunstancias. Supusimos que los mosaicos eran independientes uno de otro, es decir, para tomar un mosaico no era necesario separarlo de su vecino o algo parecido, simplemente se toma del contenedor y listo, dicho en otras palabras los datos eran independientes, a ésto se le conoce como paralelismo de datos. Por otro lado, los albañiles no se necesitaban entre ellos para pegar el mosaico que tenían a la mano, dicho de otra forma, para pegar un mosaico en la pared un solo albañil era suficiente, no necesitaba de otro que le pasara el mosaico o de alguien que pusiera la mezcla, esto quiere decir que la tarea permitía cierta independencia de los albañiles, a esto se le conoce como paralelismo de tareas. El paralelismo de datos y el de tareas son condiciones vitales para poder realizar una tarea en paralelo. Los GPU trabajan bajo ésta filosofía, ésto explica el hecho de que las supercomputadoras no son enteramente GPU, sino que cuentan con una cantidad considerable de CPU.

Pero ¿por qué los GPU fueron diseñados de esta forma?

Latencia contra rendimiento

Al principio puede parecer un poco confuso el hecho de tener dos conceptos para hablar de la eficiencia al realizar tareas. Consideremos entonces un ejemplo simple; supongamos que tenemos que llegar a una fiesta en La Paz (Baja California Sur) y la Ciudad de México, la distancia en ruta entre estas dos ciudades es de 1,498 km en coche viajando a 200 km/h el viaje nos tomaría aproximadamente 7.5 h, mientras que viajando en autobús a 50 km/h se convierte en un viaje de aproximadamente 30 horas, como el lector se habrá dado cuenta, hemos hecho los cálculos de latencia, pero, ¿qué pasaría si quisieramos hacer los cálculos de rendimiento? Pues un coche usualmente transporta a dos personas en promedio, nuestro rendimiento sería el de 0.27 personas/h mientras que en autobús considerando que usualmente se transportan 30 personas en cada autobús el rendimiento resultaría ser de 1 persona/h.


In [4]:
distancia = 1498. #distancia en kilómetros

velocidad_auto = 200. #velocidad en km/h
velocidad_bus = 50.

tiempo_auto = distancia/velocidad_auto
tiempo_bus = distancia/velocidad_bus

personas_auto = 2.
personas_bus = 30.

rendimiento_auto = personas_auto/tiempo_auto
rendimiento_bus = personas_bus/tiempo_bus

print("El coche tiene una latencia de %0.1f h y un rendimiento de %0.2f personas/h"
      %(tiempo_auto,rendimiento_auto))
print("El autobús tiene una latencia de %0.1f h y un rendimiento de %0.2f personas/h"
      %(tiempo_bus,rendimiento_bus))


El coche tiene una latencia de 7.5 h y un rendimiento de 0.27 personas/h
El autobús tiene una latencia de 30.0 h y un rendimiento de 1.00 personas/h

Con este ejemplo tan simple podemos ver que no necesariamente un proceso con latencia buena tiene un rendimiento aceptable, es por esto que debemos considerar qué queremos. Si el viaje sólo lo van a hacer dos personas, entonces es claro que la mejor opción es irse en coche; sin embargo, si viajan 15 personas (por decir algo) el autobús optimiza el proceso.

Procesadores gráficos y el surgimiento de CUDA

El cómputo en paralelo ha estado entre nosotros de una forma u otra por décadas. En sus primeros pasos, estaba confinado a usuarios con acceso a grandes y costosas computadoras. Hoy en día, las cosas son diferentes. Casi cualquier usuario que tenga en su poder una computadora de escritorio o una laptop tiene unidades centrales de procesamiento (CPU) con varios núcleos, e.g. el procesador Intel i7 que cuenta con 4 núcleos físicos y 4 núcleos lógicos. Casi todos los procesadores en teléfonos celulares y tablets cuentan con varios núcleos. La razón principal para la ubicuidad de núcleos múltiples en los CPUs se debe al fracaso de los diseñadores de procesadores para aumentar el rendimiento en diseños de solamente un núcleo, esto se lograba típicamente incrementando la velocidad del reloj, ésta última nos da una buena idea del rendimiento del procesador, e.g una computadora personal (PC) con un CPU Intel 80486 corriendo a 50 MHz será aproximadamente dos veces más rápida que una que utilice el mismo CPU y cuente con la misma memoria pero corra a 25 MHz , no siempre se cumple esta relación, pero en buena aproximación podemos decir que la velocidad del reloj nos da una buena idea de la velocidad de la computadora que lo usa; si los procesadores son de la misma familia, entonces la relación anterior es válida. Como resultado de la incapacidad para aumentar la velocidad del reloj, los fabricantes tuvieron que ingeniárselas; siendo así, desde aproximadamente 2005 los diseños de CPU han "escalado" a una mayor cantidad de núcleos, en lugar de "escalar" a mayores velocidades de reloj. Hoy en díaestán disponibles CPU que cuentan desde un par de núcleos hasta decenas de éstos, sin embargo esta candidad de núcleos en paralelo palidece ante la cantidad de núcleos disponibles en la GeForce GTX TITAN Z. Los GPU fueron diseñados como arquitecturas altamente paralelas, a mitades de la década de 1990 (una arquitectura en este sentido se entiende las partes que conforman al GPU o al CPU y cómo se relacionan dichas partes entre sí). Fueron diseñados para trabajar así principalmente debido a que el procesamiento de gráficos es una actividad inherentemente paralela. La afirmación anterior más o menos obvia si se piensa con cuidado, dado que en cierto sentido, cada pixel de la pantalla debe funcionar independientemente de sus vecinos; para ejemplificar esto necesitaremos la ayuda del plomero de la figura 2.

Figura 2: Mario en 8 bits (figura tomada de http://www.deviantart.com/)

Como podemos ver, por ejemplo la mano derecha de Mario consta de 7 cuadros, 6 de ellos forman un rectángulo y el último debe ser su pulgar y está colocado al lado del rectángulo, ahora bien, a un lado del pulgar se ve el oberol de Mario, este es rojo y no se encuentra de alguna manera relacionado con el color de la mano (a menos que esté desnudo), por lo tanto necesita libertad para mostrar cualquier color, de hecho, siguiendo este razonamiento cada uno de los pixeles de la pantalla por separado debe estar mostrando el color que el dibujante le asignó para poder obtener la imagen deseada. Las tarjetas gráficas son la justicia computacional en persona, se encargan de que las órdenes dadas por el dibujante (el sistema) sean ejecutadas en la pantalla, es por esto que los GPUs fueron diseñados para funcionar en paralelo, dado que el hecho de formar imágenes en una pantalla exige que cada pixel se encuentre trabajando de manera independiente de sus colegas.

El uso de GPU para cómputo de propósitos generales (GPGPU), fue en sus inicios una serie de retos duros de matar. Se tenía que programar a la interfaz de programación de aplicaciones gráficas (API), que demostró ser muy restrictiva en la clase de algoritmos que podían ser mapeados al GPU. Incluso cuando dicho mapeo era posible, la programación requerida para hacer esto realidad era difícil y para nada intuitiva para los científicos e ingenieros totalmente desligados a la vocación de los gráficos de computadora. Veámoslo desde el punto de vista de estas pobres almas atormentadas. Las tarjetas gráficas en ese entonces tenían órdenes muy precisas, colorea este pixel de amarillo, aquél de rojo, aquél de azul, aquél de (inserte aquí cualquiera de los demás colores que no me sé), etc. Lo que quiere decir que si se deseaba programar en la tarjeta gráfica se tenía que hacer un mapeo muy inteligente, por ejemplo declarar que un color fuera asignado a un número y luego sumar colores. Uno cree que es fácil, pero intuitivamente derivar un pixel morado y que dé uno rojo simplemente es difícil de entender, no sólo eso, se hacen muchas operaciones con colores, y el resultado es otro color, tal vez esto sería emocionante para Salvador Dalí, pero a decir verdad no era un espectáculo especialmente cautivador para los científicos. Era demasiado trabajo y esfuerzo y somos muy flojos. Esto explica por qué el proceso de adoptar a los GPUs para cómputo científico fue lento al principio.

El horizonte se iluminó para el cómputo en GPUs con la llegada de la arquitectura de NVIDIA CUDA en 2007. La arquitectura CUDA incluía tanto componentes de hardware de los GPU de NVIDIA y un ambiente de programación de software que eliminaba las barreras que limitaron la adopción del GPGPU. Desde su primera aparición en 2007, CUDA tuvo una aceptación tremenda, hasta el punto en que, en noviembre de 2010, tres de las supercomputadoras en el top cinco enlistadas en el top 500 usaban GPU. En las lista de noviembre de 2012 la supercomputadora más rápida contaba con el poder de los GPU. Una de las razones para su rápida adopción es que el modelo de programación era simple. CUDA C la primera interfaz a la arquitectura CUDA, es básicamente C con unas pocas extensiones que permiten cargar porciones de un algoritmo para ser corridas en el GPU. Es entonces un acercamiento híbrido en donde tanto GPU como CPU son usados.

Aplicaciones de CUDA

Imágen médica

Actualmente CUDA es usado en distintas áreas de la ciencia, en medicina se utiliza para detectar cáncer de mama, originalmente este era detectado por mamografía o rayos X, sin embargo frecuentemente estas prácticas llevaban a falsos positivos. La introducción de el primer GPU de NVIDIA con arquitectura CUDA junto con el lenguaje de programación CUDA C, dio una plataforma en la cual TechniScan pudo convertir los sueños de sus fundadores en realidad. Como su nombre lo indica, su sistema de imagen por ultrasonido utiliza estas ondas para dar una imagen del pecho de la paciente. El sistema de la TechniScan confía en el uso de dos tarjetas NVIDIA Tesla C1060 para procesar los 35GB de datos que se generan en un escaneo de 15 minutos. Gracias al poderío de las tarjetas Tesla, 20 minutos después el médico puede manipular una imagen muy detallada y en tres dimensiones del seno de la paciente.

Dinámica computacional de fluidos

Por muchos años, el diseño de rotores altamente eficientes y navajas permaneció como obra de artes oscuras o algo por el estilo. Básicamente debido a que el movimiento del aire o de fluidos alrededor de estos dispositivos es impresionantemente complejo, y con impresionante quiero decir tan impresionante como un cerdo karateka (tal vez CUDA también pueda darnos una simulación en eso). Esta complejidad causaba que los dispositivos no pudieran ser simulados de manera efectiva utilizando formulaciones sencillas, sin embargo las simulaciones mejor apegadas a la realidad resultaron ser demasiado costosas computacionalmente hablando (al hablar de costos aquí se quiere decir, tiempo de simulación, consumo de memoria RAM, consumo de disco duro, uso de cpu, etcétera).

Sólo las supercomputadoras más grandes del mundo podían soñar con ofrecer recursos computacionales a la altura de los sofisticados métodos numéricos requeridos para desarrollar y validar diseños. Dado que muy pocos tenían acceso a dichas computadoras, la inovación y el desarrollo de los dispositivos mencionados se mantuvo de cierta forma estancado.

La universidad de Cambridge, en una gran tradición iniciada por Charles Babbage, es hogar de grupos de investigación en el área de cómputo en paralelo. Graham Pullan y Tobias Brandvick del grupo "many-core" identificaron correctamente el potencial en la arquitectura CUDA para acelerar la dinámica computacional de fluidos a niveles impensables. Sus investigaciones iniciales indicaron que se podían alcanzar niveles aceptables de rendimiento si se usaban estaciones de trabajo (workstations) personales con GPUs integrados. Posteriormente, el uso de pequeños clusters de GPUs fácilmente superaron en rendimiento a sus costosas supercomputadoras y confirmaron las sospechas de que las capacidades de los GPUs se adecuaban bastante bien al tipo de problemas que trataban de resolver.

Para los investigadores en Cambridge, las ganancias masivas en rendimiento provistas por CUDA C representaron más que una simple mejora a sus recursos de supercómputo. La disponibilidad de cantidades grandes de GPUs de bajo costo (comparado con el de las supercomputadoras) le dio a los investigadores el poder de hacer experimentación rápida. El hecho de recibir resultados en segundos estimuló la llegada de avances importantes en esa área. Como resultado, el uso de clusters de GPUs ha transformado fundamentalmente la manera en que se hace la investigación computacional en fluidos. El tener simulaciones casi interactivas ha desatado nuevas oportunidades para la inovación y creatividad en un área de investigación anteriormente varada.

Defensa e inteligencia

La comunidad de defensa e inteligencia depende en gran manera de información detallada y a tiempo en sus operaciones estratégicas y día a día. La recolección y evaluación de los datos son partes escenciales de las actividades de inteligencia que procesa información proveniente de diversas fuentes, tales como satélites, los VANT, cámaras de vigilancia y radares. Convertir los datos crudos en información útil requiere una infraestructura importante. Los GPU representan una tecnología crucial que incrementa dramáticamente la productividad, mientras reduce el costo, energía y el uso de instalaciones.

Áreas vitales en donde los GPU están mostrando mejoras en el rendimiento son:

Procesamiento de imágenes

El papel del procesamiento de imágenes se encuentra en expansión para la defensa e inteligencia. La cantidad de imágenes disponibles el día de hoy no tiene precedentes. Por ejemplo, las imágenes geoespaciales provenientes de satélites actualmente retratan la superficie de la tierra ¡cinco veces! Existen además alrededor de 100 millones de imágenes de huellas digitales en la base de datos del FBI. Los GPU aceleran el flujo de trabajo en procesamiento de imágenes incluyendo georectificación, algoritmos de filtrado, detección de cambios y reconstrucción 3D.

Se puede encontrar más información (en inglés) en esta página.

Vigilancia por video

Los especialistas predicen que el mercado global de vigilancia por video excederá los 25 millones de dólares para 2016. Adicionalmente cada mes, el Departamento de Defensa de EEUU recolecta más de 10,000 horas de vigilancia aérea por video en Afganistán e Iraq. Dichos videos deben ser procesados y analizados en tiempo real. Los GPU representan una gran herramienta para lograr rendimiento en tiempo real para el procesamiento de videos.

Se puede encontrar más información (en inglés) en esta página.

Procesamiento de señales

Las capacidades de los sensores modernos continúan crecienco. Hacer uso de la información recolectada por dichos sensores demanda que las capacidades de cómputo también crezcan. Los GPU permiten dar un paso adelante en la velocidad de procesamiento necesaria para ir al paso de las capacidades que los sensores tienen, dando con esto una oportunidad para integración en tiempo real de datos provenientes de los sensores junto con otras fuentes de información para entender mejor los entornos complejos en que operan los equipos de defensa.

Se puede encontrar más información (en inglés) en esta página.

PyCUDA

Ahora bien, el lector se encontrará dubitativo a estar alturas, y debe estar lleno de preguntas como: ¿Es tan maravilloso CUDA? ¿Quién es ese tal PyCUDA que anuncia el título? ¿Es fácil programar en CUDA? Ya conozco un lenguaje de programación, ¿no puedo usar ese para controlar el GPU?, entre otras. Es mejor ir paso a paso, primero CUDA es maravilloso pero puede llegar a ser un dolor de cabeza, en eso no difiere de ningún lenguaje de programación conocido, posteriormente veremos que no es recomendable utilizar el GPU para todo ya que puede aumentar los costes de rendimiento en los algoritmos. No se puede utilizar cualquier lenguaje de programación (en principio) para comunicarse con el GPU, de la misma manera que no se puede encender la televisión con las llaves del auto, para mantener un dominio del GPU se necesita una interfaz que se comunique con el dispositivo, y como ya se vió CUDA es esa interfaz, aunque existen otras. Es fácil programar en CUDA, la respuesta es no, sin embargo como en la mayoría de los casos lo que lo hace difícil no es el lenguaje en sí, sino el hecho de enfrentarse con un nuevo paradigma al resolver los problemas. En resumen, lo que hace difícil a CUDA es el hecho de que los algoritmos que hagamos deben estar diseñados para ejecutarse en paralelo, y eso implica un nuevo enfoque cuando se habla de resolución de problemas.

A lo largo de esta primera parte de las notas se verán solamente cosas relacionadas con CUDA, en la segunda parte trabajaremos con PyCUDA.

PyCUDA nos permite acceder a los cálculos en paralelo de CUDA desde la interfaz de programación de Python. Y de hecho no es el único paquete que hace esto; siendo este el caso, ¿qué hace especial a PyCUDA?

  • Limpieza de los objetos ligada a su tiempo de vida.
  • Conveniencia. Abstracciones como pycuda.driver.SourceModule y pycuda.gpuarray.GPUArray hacen la programación en CUDA aún más conveniente que simplemente programando en CUDA C.
  • Completitud. PyCUDA pone todo el poder del controlador de CUDA a nuestra disposición, si lo deseas.
  • Verificación automática de errores. Todos los errores de CUDA son traducidos a excepciones de Python.
  • Velocidad.
  • Tenemos acceso al código fuente.
  • Documentación extensiva.

Por el momento muchas de estas cosas no quedarán claras, pero se le sugiere al lector volver a este punto después de terminar la parte I, y de nuevo al terminar la parte II.

Materiales adicionales